package cn.barudisshu.filter;

import cn.barudisshu.handler.Config;
import cn.barudisshu.handler.Handler;
import cn.barudisshu.handler.InitException;
import cn.barudisshu.handler.RestIniter;
import cn.barudisshu.http.servlet.HttpRequest;
import cn.barudisshu.http.servlet.HttpResponse;
import cn.barudisshu.util.AntPathMatcher;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.annotation.WebInitParam;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;

/**
 * @author galudisu
 */
@WebFilter(
        filterName = "restFilter",
        urlPatterns = "/*",
        initParams = {
                @WebInitParam(name = "configClass",value = "cn.barudisshu.sample.AppConfig")
})
public class RestFilter implements Filter {

    private final static Logger logger = LoggerFactory.getLogger(RestFilter.class);

    public static final String PARAM_NAME_CONFIGCLASS = "configClass";
    public static final String PARAM_NAME_EXCLUSIONS = "exclusions";

    private RestIniter restIniter;
    private static final String encoding = "UTF-8";
    private Set<String> excludesPattern;
    private Handler handler;

    public void init(FilterConfig filterConfig) throws ServletException {
        String exclusions = filterConfig.getInitParameter(PARAM_NAME_EXCLUSIONS);
        if(exclusions != null && exclusions.trim().length() != 0){
            excludesPattern = new HashSet<>(Arrays.asList(exclusions.split("\\s*,\\s*")));
        }

        try {
            Config config = createConfig(filterConfig.getInitParameter(PARAM_NAME_CONFIGCLASS));
            restIniter = new RestIniter(filterConfig.getServletContext(), config);
            handler = restIniter.getHandler();
        }catch (Exception e){
            e.printStackTrace();
            throw new ServletException();
        }
    }

    public boolean isExclusion(String requestURI) {
        if (excludesPattern == null) {
            return false;
        }
        for (String pattern : excludesPattern) {
            if (AntPathMatcher.instance().matches(pattern, requestURI)) {
                return true;
            }
        }
        return false;
    }

    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        if (!(request instanceof HttpServletRequest)
                || !(response instanceof HttpServletResponse)) {
            throw new ServletException("Guas doesn't support non-HTTP request or response.");
        }
        request.setCharacterEncoding(encoding);
        response.setCharacterEncoding(encoding);

        HttpRequest req = new HttpRequest((HttpServletRequest) request, restIniter.getServletContext());
        HttpResponse res = new HttpResponse((HttpServletResponse) response, (HttpServletRequest) request);

        boolean isHandled = false;
        if (!isExclusion(req.getRestPath())) {
            try {
                handler.handle(req, res, isHandled);
            } catch (Exception e) {
                // todo: hold exceptions, and response error message
            } finally {
                res.close();
            }
        }

/*
        if (!isHandled) {
            // chain to next
            chain.doFilter(request, response);
        }
*/
    }

    public void destroy() {

    }
    /**
     * 读取配置类
     *
     * @param configClass class地址
     * @return Config
     */
    private Config createConfig(String configClass) {
        Config config = null;
        if (configClass != null) {
            Object temp = null;
            try {
                temp = Class.forName(configClass).newInstance();
            } catch (Exception e) {
                throw new InitException("Could not create instance of class: " + configClass, e);
            }
            if (temp instanceof Config) {
                config = (Config) temp;
            } else {
                throw new InitException("Could not create instance of class: " + configClass + ". Please check the init in web.xml");
            }
        } else {
            config = new Config();
            logger.warn("Could not found init and start in no init.");
        }
        return config;
    }

}
